package indi.zhifa.study2024.nbr.logic.curd.consign.service.impl;


import indi.zhifa.core.common.util.DtoEntityUtil;
import indi.zhifa.core.common.web.entity.exception.ServiceException;
import indi.zhifa.core.mpenhance.utils.DbDtoEntityUtil;
import indi.zhifa.study2024.nbr.gen.busy.consign.service.IGenConsignDetailSimpleDbService;
import indi.zhifa.study2024.nbr.gen.busy.consign.service.IGenConsignHeadDbService;
import indi.zhifa.study2024.nbr.gen.busy.consign.entity.GenConsignDetailSimpleEntity;
import indi.zhifa.study2024.nbr.gen.busy.consign.entity.GenConsignHeadEntity;
import indi.zhifa.study2024.nbr.logic.curd.consign.entity.request.ConsignBillCreateRequest;
import indi.zhifa.study2024.nbr.logic.curd.consign.entity.request.ConsignBillDetailCreateRequest;
import indi.zhifa.study2024.nbr.logic.curd.consign.entity.request.ConsignBillDetailModifyRequest;
import indi.zhifa.study2024.nbr.logic.curd.consign.entity.request.ConsignBillModifyRequest;
import indi.zhifa.study2024.nbr.logic.curd.consign.entity.response.ConsignDetailSimpleVo;
import indi.zhifa.study2024.nbr.logic.curd.consign.entity.response.ConsignVo;
import indi.zhifa.study2024.nbr.logic.curd.consign.service.IConsignService;
import indi.zhifa.study2024.nbr.gen.busy.item.service.IGenItemDefineDbService;
import indi.zhifa.study2024.nbr.gen.busy.item.entity.GenItemDefineEntity;
import indi.zhifa.study2024.nbr.gen.busy.storage.service.IGenItemInventoryDbService;
import indi.zhifa.study2024.nbr.gen.busy.storage.entity.GenItemInventoryEntity;
import indi.zhifa.study2024.nbr.enums.bill.EBillStatus;
import indi.zhifa.study2024.nbr.enums.bill.consign.ESaleType;
import indi.zhifa.study2024.nbr.util.billNo.BillNoUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@Service
public class ConsignServiceImpl implements IConsignService {

    final IGenConsignHeadDbService mConsignHeadDbService;
    final IGenConsignDetailSimpleDbService mConsignDetailSimpleDbService;
    final IGenItemDefineDbService mItemDefineDbService;
    final IGenItemInventoryDbService mItemInventoryDbService;
    final BillNoUtil mBillNoUtil;

    protected GenConsignDetailSimpleEntity genConsignDetailSimpleEntity(GenConsignHeadEntity pConsignHeadEntity,
                                                                        ConsignBillDetailCreateRequest pConsignBillDetailCreateRequest){
        GenConsignDetailSimpleEntity simpleEntity = new GenConsignDetailSimpleEntity();
        simpleEntity.createInit();
        simpleEntity.setBillId(pConsignHeadEntity.getId());

        GenItemDefineEntity itemDefineEntity = mItemDefineDbService.check(pConsignBillDetailCreateRequest.getItemId());
        GenItemInventoryEntity itemInventoryEntity = mItemInventoryDbService.findByItemId(itemDefineEntity.getId());

        simpleEntity.setItemId(pConsignBillDetailCreateRequest.getItemId());
        simpleEntity.setItemName(itemDefineEntity.getName());
        simpleEntity.setLargeUnitCnt(pConsignBillDetailCreateRequest.getLargeUnitCnt());
        simpleEntity.setMidUnitCnt(pConsignBillDetailCreateRequest.getMidUnitCnt());
        simpleEntity.setSmallUnitCnt(pConsignBillDetailCreateRequest.getSmallUnitCnt());

        long saleCount = 0;
        BigDecimal saleCountLarge;
        switch (itemDefineEntity.getUnitType()) {
            case SINGLE_UNIT -> {
                saleCount = pConsignBillDetailCreateRequest.getLargeUnitCnt();
                saleCountLarge = BigDecimal.valueOf(saleCount).setScale(3, RoundingMode.HALF_UP);
            }
            case NORMAL_UNIT -> {
                saleCount = (long) pConsignBillDetailCreateRequest.getLargeUnitCnt() * itemDefineEntity.getLargeConvert()
                        + pConsignBillDetailCreateRequest.getSmallUnitCnt();
                saleCountLarge = BigDecimal.valueOf(saleCount).divide(BigDecimal.valueOf(itemDefineEntity.getLargeConvert()),3, RoundingMode.HALF_UP);
            }
            case TRIPLE_UNIT -> {
                saleCount = (long) pConsignBillDetailCreateRequest.getLargeUnitCnt() * itemDefineEntity.getLargeConvert()
                        + (long) pConsignBillDetailCreateRequest.getMidUnitCnt() * itemDefineEntity.getMidConvert()
                        + pConsignBillDetailCreateRequest.getSmallUnitCnt();
                saleCountLarge = BigDecimal.valueOf(saleCount).divide(BigDecimal.valueOf(itemDefineEntity.getLargeConvert()),3, RoundingMode.HALF_UP);
            }
            default -> {
                saleCountLarge = BigDecimal.valueOf(saleCount).setScale(3, RoundingMode.HALF_UP);
            }
        }



        Long amount = pConsignBillDetailCreateRequest.getAmount();
        BigDecimal priceLarge;
        if(null == amount || 0 == amount){
            priceLarge = pConsignBillDetailCreateRequest.getPriceLarge();
            if(null == priceLarge || 0 == priceLarge.compareTo(BigDecimal.ZERO)){
                if(pConsignBillDetailCreateRequest.getSaleType() != ESaleType.PRESENT){
                    throw new ServiceException("商品"+itemDefineEntity.getId()+"没有提供价格，且不是赠送商品");
                }
            }
            amount = priceLarge.multiply(saleCountLarge).multiply(BigDecimal.valueOf(100)).setScale(0,RoundingMode.HALF_UP).longValue();
        }else{
            priceLarge = BigDecimal.valueOf(amount).divide(BigDecimal.valueOf(100),4,RoundingMode.HALF_UP);
        }
        priceLarge.setScale(4, RoundingMode.HALF_UP);
        simpleEntity.setPriceLarge(priceLarge);
        simpleEntity.setCostSmall(itemInventoryEntity.getCurPrice());
        simpleEntity.setCostMid(itemInventoryEntity.getCurPrice().multiply(BigDecimal.valueOf(itemDefineEntity.getMidConvert())));
        simpleEntity.setCostLarge(itemInventoryEntity.getCurPriceLarge());

        switch (itemDefineEntity.getUnitType()) {
            case SINGLE_UNIT -> {
                simpleEntity.setPriceSmall(priceLarge);
            }
            case NORMAL_UNIT -> {
                simpleEntity.setPriceSmall(priceLarge.divide(BigDecimal.valueOf(itemDefineEntity.getLargeConvert()),4, RoundingMode.HALF_UP));
            }
            case TRIPLE_UNIT -> {
                simpleEntity.setPriceSmall(priceLarge.divide(BigDecimal.valueOf(itemDefineEntity.getLargeConvert()),4, RoundingMode.HALF_UP));
                simpleEntity.setPriceMid(simpleEntity.getPriceSmall().multiply(BigDecimal.valueOf(itemDefineEntity.getMidConvert())));
            }
        }

        simpleEntity.setCost(itemInventoryEntity.getCurPriceLarge().multiply(saleCountLarge)
                .multiply(BigDecimal.valueOf(100)).setScale(0,RoundingMode.HALF_UP).longValue());

        // 检测库存
        if(itemInventoryEntity.getStorage() < saleCount){
            throw new ServiceException("商品"+itemInventoryEntity.getItemName()+"库存不足");
        }

        simpleEntity.setCount(saleCount);
        simpleEntity.setSaleType(pConsignBillDetailCreateRequest.getSaleType());
        simpleEntity.setAmount(amount);

        return simpleEntity;
    }

    @Transactional(rollbackFor = Exception.class)
    public void reviewBill(GenConsignHeadEntity pConsignHeadEntity, List<GenConsignDetailSimpleEntity> pConsignDetailEntityList){
        pConsignHeadEntity.setBillStatus(EBillStatus.AUDITED);

        List<Long> itemIdList = pConsignDetailEntityList.stream().map(GenConsignDetailSimpleEntity::getItemId).distinct().collect(Collectors.toList());
        List<GenItemInventoryEntity> itemInventoryEntities = mItemInventoryDbService.listByItemIdList(itemIdList);
        Map<Long, GenItemInventoryEntity> itemInventoryMap = itemInventoryEntities.stream()
                .collect(Collectors.toMap(GenItemInventoryEntity::getItemId, it->it));

        List<GenItemInventoryEntity> inventoryEntityList = mItemInventoryDbService.listByItemIdList(itemIdList);
        for(GenConsignDetailSimpleEntity cds : pConsignDetailEntityList){
            Long itemId = cds.getItemId();
            GenItemInventoryEntity itemInventoryEntity = itemInventoryMap.get(itemId);
            itemInventoryEntity.setStorage(itemInventoryEntity.getStorage() - cds.getCount());
            inventoryEntityList.add(itemInventoryEntity);
        }
        mItemInventoryDbService.updateBatchById(inventoryEntityList);
        mConsignHeadDbService.updateById(pConsignHeadEntity);

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ConsignVo create(ConsignBillCreateRequest pConsignBillRequest) {

        GenConsignHeadEntity consignHeadEntity = new GenConsignHeadEntity();
        consignHeadEntity.createInit();

        consignHeadEntity.setBillType(pConsignBillRequest.getBillType());
        consignHeadEntity.setSalesId(pConsignBillRequest.getSalesId());
        consignHeadEntity.setSalesName(pConsignBillRequest.getSalesName());

        if(pConsignBillRequest.getBillTime() != null){
            LocalDateTime billTime = pConsignBillRequest.getBillTime();
            Instant instantBeg = billTime.toInstant(ZoneOffset.UTC);
            consignHeadEntity.setBillTime(instantBeg.getEpochSecond());
        }else{
            consignHeadEntity.setBillTime(System.currentTimeMillis()/1000);
        }


        consignHeadEntity.setCustomerId(pConsignBillRequest.getCustomerId());
        consignHeadEntity.setCustomerName(pConsignBillRequest.getCustomerName());

        Long cost = 0L;
        Long amount = 0L;
        Long profit = 0L;
        List<ConsignBillDetailCreateRequest> items = pConsignBillRequest.getItems();
        List<GenConsignDetailSimpleEntity> simpleEntityList = new ArrayList<>();
        for(ConsignBillDetailCreateRequest item : items) {
            GenConsignDetailSimpleEntity simpleEntity = genConsignDetailSimpleEntity(consignHeadEntity, item);
            simpleEntityList.add(simpleEntity);
            cost += simpleEntity.getCost();
            amount += simpleEntity.getAmount();
        }
        profit = amount - cost;

        consignHeadEntity.setCost(cost);
        consignHeadEntity.setProfit(profit);
        consignHeadEntity.setAmount(amount);

        // 为简单起见，这里直接就给审核了
        consignHeadEntity.setBillStatus(EBillStatus.AUDITED);
        reviewBill(consignHeadEntity,simpleEntityList);

        String billNo = mBillNoUtil.getBillNo("csn");
        consignHeadEntity.setBillNo(billNo);
        mConsignHeadDbService.save(consignHeadEntity);
        simpleEntityList.forEach(item->item.setBillNo(billNo));

        mConsignDetailSimpleDbService.saveBatch(simpleEntityList);

        ConsignVo consignVo = DtoEntityUtil.trans(consignHeadEntity, ConsignVo.class);

        List<ConsignDetailSimpleVo> consignDetailSimpleVoList = DtoEntityUtil.trans(simpleEntityList, ConsignDetailSimpleVo.class);
        consignVo.setItems(consignDetailSimpleVoList);

        return consignVo;
    }

    @Override
    public ConsignVo info(Long pId) {
        GenConsignHeadEntity consignHeadEntity = mConsignHeadDbService.check(pId);
        List<GenConsignDetailSimpleEntity> items = mConsignDetailSimpleDbService.listByBillId(pId);
        ConsignVo rtn = DtoEntityUtil.trans(consignHeadEntity, ConsignVo.class);
        List<ConsignDetailSimpleVo> consignDetailSimpleVoList = DtoEntityUtil.trans(items, ConsignDetailSimpleVo.class);
        rtn.setItems(consignDetailSimpleVoList);
        return rtn;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public GenConsignHeadEntity changeStatus(Long pId, EBillStatus pEBillStatus) {
        GenConsignHeadEntity consignHeadEntity = mConsignHeadDbService.check(pId);
        consignHeadEntity.setBillStatus(pEBillStatus);
        mConsignHeadDbService.updateById(consignHeadEntity);
        return consignHeadEntity;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ConsignVo edit(Long pId, ConsignBillModifyRequest pConsignBillModifyRequest) {
        GenConsignHeadEntity consignHeadEntity = mConsignHeadDbService.check(pId);
        GenConsignHeadEntity newConsignHeadEntity = DbDtoEntityUtil.editByDto(consignHeadEntity,pConsignBillModifyRequest, GenConsignHeadEntity.class);
        List<GenConsignDetailSimpleEntity> simpleEntityList = mConsignDetailSimpleDbService.listByBillId(pId);
        Map<Long, GenConsignDetailSimpleEntity> simpleEntityMap = simpleEntityList.stream().collect(Collectors.toMap(GenConsignDetailSimpleEntity::getId, e->e));
        List<GenConsignDetailSimpleEntity> createConsignDetailSimpleEntityList = new ArrayList<>();
        List<GenConsignDetailSimpleEntity> modifyConsignDetailSimpleEntityList = new ArrayList<>();

        for(ConsignBillDetailModifyRequest simpleEntityRequest : pConsignBillModifyRequest.getItems()) {
            if(null == simpleEntityRequest.getId()){
                ConsignBillDetailCreateRequest consignBillDetailCreateRequest = DtoEntityUtil.trans(simpleEntityRequest, ConsignBillDetailCreateRequest.class);
                GenConsignDetailSimpleEntity consignDetailSimpleEntity = genConsignDetailSimpleEntity(newConsignHeadEntity,consignBillDetailCreateRequest);
                createConsignDetailSimpleEntityList.add(consignDetailSimpleEntity);
            }else{
                GenConsignDetailSimpleEntity consignDetailSimpleEntity = simpleEntityMap.get(simpleEntityRequest.getId());
                if(null == consignDetailSimpleEntity){
                    continue;
                }

                GenConsignDetailSimpleEntity newConsignDetailSimpleEntity = DbDtoEntityUtil.editByDto(consignDetailSimpleEntity,
                        simpleEntityRequest, GenConsignDetailSimpleEntity.class);
                modifyConsignDetailSimpleEntityList.add(newConsignDetailSimpleEntity);
            }
        }
        mConsignHeadDbService.updateById(newConsignHeadEntity);
        mConsignDetailSimpleDbService.saveBatch(createConsignDetailSimpleEntityList);
        mConsignDetailSimpleDbService.updateBatchById(modifyConsignDetailSimpleEntityList);

        List<GenConsignDetailSimpleEntity> wholeItem = new ArrayList<>();
        wholeItem.addAll(createConsignDetailSimpleEntityList);
        wholeItem.addAll(modifyConsignDetailSimpleEntityList);
        ConsignVo rtn = DtoEntityUtil.trans(consignHeadEntity, ConsignVo.class);
        List<ConsignDetailSimpleVo> consignDetailSimpleVoList = DtoEntityUtil.trans(wholeItem, ConsignDetailSimpleVo.class);
        rtn.setItems(consignDetailSimpleVoList);
        return rtn;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ConsignVo delete(Long pId) {
        ConsignVo consignVo = info(pId);
        mConsignHeadDbService.removeById(consignVo.getId());
        mConsignDetailSimpleDbService.removeByIds(consignVo.getItems().stream().map(GenConsignDetailSimpleEntity::getId).collect(Collectors.toList()));
        return consignVo;
    }
}
